package com.ming.common.liteflow.context;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.ming.common.liteflow.core.dynamic.IvyDynamicClass;
import com.ming.common.liteflow.core.el.Flow;
import com.ming.common.liteflow.core.el.IvyEl;
import com.ming.common.liteflow.core.enums.LiteFlowEnums;
import com.ming.common.liteflow.core.execption.LiteFlowELException;
import com.ming.common.liteflow.core.graph.Node;
import com.ming.common.liteflow.core.node.CmpScriptUtil;
import com.ming.common.liteflow.core.node.IvyCmp;
import com.ming.common.util.FileUtil;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.query.LambdaQuery;
import org.springframework.util.ClassUtils;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IvyLoadCmp {

    public static void loadCmp(IvyEl ivyEl){
//        SQLManager sqlManager = SpringUtil.getBean("sqlManager");
//        LambdaQuery<NodeInfo> query = sqlManager.lambdaQuery(NodeInfo.class);
//        List<NodeInfo> nodeInfoList = query.select();
//        LambdaQuery<IvyDynamicClass> lambdaQuery = sqlManager.lambdaQuery(IvyDynamicClass.class);
//        List<IvyDynamicClass> dynamicClassList = lambdaQuery.select();
        try {
            Flow flow = Flow.NEW().logicFlow(ivyEl.getSourceJson());
            List<Node> flowNodeList = flow.flowNodeList();
            List<Node> fallbackList = flow.fallbackList();
            flowNodeList.forEach(m -> loadCmpByNode(m));
            fallbackList.forEach(m -> loadCmpByNode(m));
        } catch (LiteFlowELException e) {
            e.printStackTrace();
        }
    }

    public static void loadCmpByNode(Node node){
        IvyCmp nodeInfo = node.getProperties();
        buildCmpByNodeInfo(nodeInfo);
    }

    private static void buildCmpByNodeInfo(IvyCmp nodeInfo){
        String clazz = nodeInfo.getClazz();
        String language = nodeInfo.getLanguage();
        String script = nodeInfo.getScript();
        if(StrUtil.isNotBlank(clazz)){
            buildCmpByClass(nodeInfo);
        } else if(StrUtil.isNotBlank(language) && StrUtil.isNotBlank(script)){
            buildCmpByScript(nodeInfo, CmpScriptUtil.getScriptEnum(nodeInfo.getType()));
        } else {
            nodeInfo.setType(NodeTypeEnum.SCRIPT.getCode());
            nodeInfo.setLanguage("java");
            buildCmpByScript(nodeInfo, LiteFlowEnums.SCRIPT_JAVA.script);
        }
    }

    private static void buildCmpByClass(IvyCmp nodeInfo){
        LiteFlowNodeBuilder builder = CmpScriptUtil.getLiteFlowNodeBuilder(nodeInfo.getType());
        if(builder != null){
            String clazz = nodeInfo.getClazz();
            LoadingCache<String, Map<String, Class<?>>> classCache = SpringUtil.getBean("dynamicClassCache");
            Map<String, Class<?>> classMap = classCache.get("any");
            builder = builder.setId(nodeInfo.getComponentId()).setName(nodeInfo.getComponentName());
            if(classMap != null && classMap.containsKey(clazz)){
                builder.setClazz(classMap.get(clazz));
            }else{
                builder.setClazz(clazz);
            }
            builder.build();
        }
    }

    private static void buildCmpByScript(IvyCmp nodeInfo,LiteFlowEnums.SCRIPT_JAVA script){
        LiteFlowNodeBuilder liteFlowNodeBuilder = CmpScriptUtil.getLiteFlowNodeBuilder(nodeInfo.getType());
        if(liteFlowNodeBuilder != null){
            liteFlowNodeBuilder.setId(nodeInfo.getComponentId())
                    .setName(nodeInfo.getComponentName())
                    .setLanguage(nodeInfo.getLanguage() == null ? script.getLanguage() : nodeInfo.getLanguage())
                    .setClazz(nodeInfo.getClazz())
                    .setScript(nodeInfo.getScript() == null ? script.getScriptStr() : nodeInfo.getScript())
                    .setType(script.getNodeTypeEnum())
                    .build();
        }
    }

    private static final Map<String,Integer> dynamicClassMap = new HashMap<>();

    public static Map<String,Class<?>> loadClassAll(){
        SQLManager sqlManager = SpringUtil.getBean("sqlManager");
        LambdaQuery<IvyDynamicClass> query = sqlManager.lambdaQuery(IvyDynamicClass.class);
        List<IvyDynamicClass> list = query.select();
        Map<String,Class<?>> classMap = new HashMap<>();
        list.forEach(m->{
            classMap.put(m.getClassId(), IvyLoadCmp.loadClass(m));
        });
        return classMap;
    }

//    public static Class<?> loadClass(IvyDynamicClass item,String sourceCode){
//        String beanName = item.getSourceClassName();
//        dynamicClassMap.putIfAbsent(beanName,item.getVersion());
//        // 判断源码是否发生变化
//        if(!sourceCode.equals(item.getSourceCode())){ // 不一致
//            item.setSourceCode(item.getSourceCode());
//            Integer version = dynamicClassMap.get(beanName);
//            version = version + 1;
//            item.setVersion(version);
//            dynamicClassMap.put(beanName, version);
//        }
//        return compilerCmp(item);
//    }
    public static Class<?> loadClass(IvyDynamicClass item){
        String beanName = item.getSourceClassName();
        dynamicClassMap.putIfAbsent(beanName,item.getVersion());
        // 判断源码是否发生变化
        /*if(!item.getSourceCode().equals(item.getSourceCode())){ // 不一致
            item.setSourceCode(item.getSourceCode());
            Integer version = dynamicClassMap.get(beanName);
            version = version + 1;
            item.setVersion(version);
            dynamicClassMap.put(beanName, version);
        }*/
        return compilerCmp(item);
    }

    private static Class<?> compilerCmp(IvyDynamicClass ivyDynamicClass) {
        String root = ClassUtils.getDefaultClassLoader().getResource("").getPath();
        String className = ivyDynamicClass.getSourceClassName()+ivyDynamicClass.getVersion();
        String packagePath = String.join(File.separator,ivyDynamicClass.getPackagePath().split("\\."));
        String rootPath = root+packagePath;
        String forName = ivyDynamicClass.getPackagePath()+"."+className;
        String sourceCode = ivyDynamicClass.getSourceCode();

        Pattern pattern = Pattern.compile("class\\s+(\\w+)");
        Matcher matcher = pattern.matcher(sourceCode);
        sourceCode = matcher.replaceAll("class " + className);

        pattern = Pattern.compile("@LiteflowComponent\\(\"([^\"]+)\"\\)");
        matcher = pattern.matcher(sourceCode);
        sourceCode = matcher.replaceAll("@LiteflowComponent(\"" + className+"\")");
        sourceCode = sourceCode.replaceAll(ivyDynamicClass.getSourceClassName()+".class",className+".class");

        Class<?> dynamicClass = null;
        try {
            // 检查并创建根路径
            File rootDir = new File(rootPath);
            if (!rootDir.exists()) {
                if (!rootDir.mkdirs()) {
                    System.err.println("无法创建动态class目录");
                    System.exit(1);
                }
            }

            String fileClassName = className + ".class";
            String fileJavaName = className + ".java";
            FileUtil.removeFile(rootPath,fileClassName,fileJavaName);

            // 确保输出目录存在
            File outputDir = new File(root);

            // 保存源码到文件
            File sourceFile = new File(rootDir, className + ".java");
            FileWriter writer = new FileWriter(sourceFile);
            writer.write(sourceCode);
            writer.close();

            // 获取系统编译器
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

            // 获取文件管理器
            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

            // 获取编译单元
            Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));

            // 设置编译参数
            Iterable<String> options = Arrays.asList("-d", outputDir.getAbsolutePath());

            // 创建编译任务
            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, compilationUnits);

            // 执行编译任务
            boolean success = task.call();

            if (success) {
                // 加载编译后的类
                URLClassLoader classLoader = new URLClassLoader(new URL[]{new File(root).toURI().toURL()});
                dynamicClass = Class.forName(forName, true, classLoader);

                // 创建实例并调用方法
                //Object instance = dynamicHelloClass.getDeclaredConstructor().newInstance();
                //dynamicHelloClass.getMethod(methodName).invoke(instance);
            }

            // 关闭文件管理器
            fileManager.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dynamicClass;
    }

    private static String getClassName(String sourceCode){
        Pattern pattern = Pattern.compile("class\\s+(\\w+)");
        Matcher matcher = pattern.matcher(sourceCode);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    private static String getCmpId(String sourceCode){
        Pattern pattern = Pattern.compile("@LiteflowComponent\\(\"([^\"]+)\"\\)");
        Matcher matcher = pattern.matcher(sourceCode);
        if (matcher.find()) {
            return  matcher.group(1);
        }
        return null;
    }

    private static String getPackagePath(String sourceCode){
        // 定义正则表达式匹配包名
        String packageNameRegex = "\\bpackage\\s+([\\w.]+)\\s*;";
        // 编译正则表达式
        Pattern pattern = Pattern.compile(packageNameRegex);
        // 创建 Matcher 对象并匹配源代码
        Matcher matcher = pattern.matcher(sourceCode);
        // 提取包名
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

}
